import logging from '../logging'
import {isEmpty} from '../utils'
import os from 'os'
import * as uuid from 'uuid'
import path from 'path'
import fs from 'fs'
import axios, {AxiosError} from 'axios';

/**
 * 不依赖外部框架的 http headers 类型
 */
type Headers = Record<string, string | string[]>;

/**
 * 根据toolUrl下载一个tool到指定的目录下.
 * @param toolUrl 文件下载Url
 * @param headers 下载指定的Header，Optional
 * @param destDir 下载指定的目标目录，Optional
 * @returns {Promise<void>} 下载以后的文件路径
 */
export async function download(
    toolUrl: string,
    headers?: Headers,
    destDir?: string
): Promise<string> {
    if (isEmpty(destDir)) {
        destDir = path.join(os.tmpdir(), uuid.v4())
    }
    fs.mkdirSync(destDir as string, {recursive: true})
    const downloadDestPath: string = path.join(
        destDir as string,
        `${uuid.v4()}.tool.tmp`
    )
    fs.writeFileSync(downloadDestPath, '')

    const axiosHeaders = new axios.AxiosHeaders()
    if (headers != undefined) {
        Object.entries(headers).forEach(([key, value]) => {
            axiosHeaders.set(key, value)
        });
    }

    return await new Promise(function (resolve, reject) {
        axios.request({
            method: 'GET',
            url: toolUrl,
            headers: axiosHeaders,
            responseType: 'stream',
            onDownloadProgress: (progressEvent) => {
                if (progressEvent.total !== undefined){
                    const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total);
                    logging.info(`Download progress: ${percentCompleted}%`);
                }
            }
        }).then(function (response) {
            const writer = fs.createWriteStream(downloadDestPath)
            response.data.pipe(writer)
            response.data.on('error', function (err: Error) {
                reject(err)
            })
            writer.on('finish', function () {
                resolve(downloadDestPath)
            })
        }).catch(error => {
            if (error.response) {
                // The request was made and the server responded with a status code
                // that falls out of the range of 2xx
                reject(new Error(`Request failed with status code ${error.response.status}`));
            } else {
                // Something happened in setting up the request that triggered an Error
                reject(new Error(`Request failed with error ${error.message}`));
            }
        });
    })
}

/**
 * 根据toolUrl下载一个tool到指定的目录下.
 * @param toolUrl 文件下载Url
 * @param headers 下载指定的Header，Optional
 * @param destDir 下载指定的目标目录，Optional
 * @param maxRetires 最大重试次数
 * @returns {Promise<void>} 下载以后的文件路径
 */
export async function downloadWithRetries(
    toolUrl: string,
    headers?: Headers,
    destDir?: string,
    maxRetires?: number
): Promise<string> {
    if (maxRetires == null) {
        maxRetires = 3
    }
    let retriesTimes = 0
    while (retriesTimes < maxRetires) {
        try {
            return await download(toolUrl, headers, destDir)
        } catch (err) {
            logging.warning(
                `Downloading tool from ${toolUrl} failed, ${err}, retry later...`
            )
        }

        retriesTimes++
    }

    logging.error(`Downloading tool from ${toolUrl} failed, abort download.`)
    throw new Error(`downloading tool from ${toolUrl} failed, abort download.`)
}

export async function requestURL(
    url: string,
    timeout?: number
): Promise<string> {
    return new Promise<string>((resolve, reject) => {
        axios.get(url, {timeout: timeout})
            .then(response => {
                resolve(response.data);
            })
            .catch((error: AxiosError) => {
                if (error.code === 'ECONNREFUSED') {
                    reject(`Get ${url} ECONNREFUSED`);
                } else if (error.code === 'ECONNABORTED') {
                    reject(`Get ${url} ECONNABORTED`);
                } else {
                    reject(`Get ${url} error: ${error.code} ${error.message}`);
                }
            });
    });
}